package com.xh.customview.views;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.BitmapDrawable;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.widget.Scroller;

import com.xh.customview.R;
import com.xh.customview.utils.Log;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

/**
 * @author xionghg
 * @email xiong9394@gmail.com
 * @created 2018-03-04.
 */

public class RoundImagesPickView extends View {
    private static final String TAG = "RoundImagesPickView";

    // 最大图片数
    private static final int MAX_PARTS = 8;
    Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    // 椭圆中心点 x 坐标
    private int mOvalCenterX;
    // 椭圆中心点 y 坐标
    private int mOvalCenterY;
    // 椭圆长轴长度
    private int mOvalLongAxis;
    // 椭圆短轴长度
    private int mOvalShortAxis;

    // 图片数量
    private int mDrawableCount;
    // 图片宽度
    private int mDrawableWidth;
    // 图片高度
    private int mDrawableHeight;
    // 所有图片信息
    private List<DrawableInfo> mDrawableList = new ArrayList<>();
    // 旋转角度，0-359
    private int mRotateAngle;

    private int mLastAngle;

    private Scroller mScroller;
    private VelocityTracker mVelocityTracker;

    private DrawableInfo[] mArrayType = new DrawableInfo[0];

    public RoundImagesPickView(Context context) {
        super(context);
        Log.d(TAG, "RoundImagesPickView: constructor1");
    }

    public RoundImagesPickView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
        Log.d(TAG, "RoundImagesPickView: constructor2");
    }

    public RoundImagesPickView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        Log.d(TAG, "RoundImagesPickView: constructor3");

        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RoundImagesPickView);
        int resId = a.getResourceId(R.styleable.RoundImagesPickView_images, R.array.round_images_pick_default_array);
        Log.d(TAG, "RoundImagesPickView: resId=" + Integer.toHexString(resId));
        int[] ids = context.getResources().getIntArray(resId);
        updateDrawableIds(context, ids);
        a.recycle();

        Log.d(TAG, "RoundImagesPickView: ids=" + Arrays.toString(ids));
        mScroller = new Scroller(getContext());
        mVelocityTracker = VelocityTracker.obtain();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        // 待定
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);

        if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(200, 200);
        } else if (widthSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(200, heightSpecSize);
        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(widthSpecSize, 200);
        }
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        Log.d(TAG, "onLayout: changed=" + changed + ", left=" + left + ", top=" + top +
                ", right=" + right + ", bottom=" + bottom);
        // 更新虚拟椭圆信息
        if (changed) {
            mOvalCenterX = (left + right) / 2;
            mOvalCenterY = top + (bottom - top) * 2 / 5;

            mOvalLongAxis = 3 * (right - left) / 8;
            mOvalShortAxis = mOvalLongAxis / 2;

            mDrawableWidth = getMeasuredWidth() / 2;
            mDrawableHeight = getMeasuredHeight() / 2;
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        updateImageInfos();

        DrawableInfo[] images = mDrawableList.toArray(mArrayType);
        Arrays.sort(images, new Comparator<DrawableInfo>() {
            @Override
            public int compare(DrawableInfo o1, DrawableInfo o2) {
                return o1.orderAngle - o2.orderAngle;
            }
        });

        for (int i = 0; i < mDrawableCount; i++) {
            canvas.save();
            DrawableInfo info = images[i];
            canvas.clipRect(info.left, info.top, info.right, info.bottom);
            if (info.color != 0) {
                canvas.drawColor(info.color);
            } else {
                canvas.drawBitmap(((BitmapDrawable) getContext().getResources().getDrawable(info.drawableId)).getBitmap(), info.left, info.top, null);
            }
            canvas.restore();
        }
    }

    private void updateImageInfos() {
        int[] angles = spiltRoundToAngles(180 + mRotateAngle, mDrawableCount);
        for (int i = 0; i < mDrawableCount; i++) {
            DrawableInfo drawableInfo = mDrawableList.get(i);
            drawableInfo.setRealAngle(angles[i]);

            drawableInfo.centerY = mOvalCenterY + (int) (2 * mOvalShortAxis * (drawableInfo.rateOnY - 0.5));
            int temp = (int) Math.pow((1 - (drawableInfo.centerY - mOvalCenterY) * (drawableInfo.centerY - mOvalCenterY) /
                    (double) (mOvalShortAxis * mOvalShortAxis)) * mOvalLongAxis * mOvalLongAxis, 0.5);
            if (drawableInfo.realAngle <= 180) {
                drawableInfo.centerX = mOvalCenterX + temp;
            } else {
                drawableInfo.centerX = mOvalCenterX - temp;
            }

            int width = (int) ((0.5 + 0.5 * drawableInfo.rateOnY) * mDrawableWidth);
            int height = (int) ((0.5 + 0.5 * drawableInfo.rateOnY) * mDrawableHeight);
            drawableInfo.setPosition(width, height);
            Log.d(TAG, "updateImageInfos: " + i + ": " + drawableInfo.toString());
        }
    }

    private int mLastX;
    private int mLastY;
    private boolean mAboveOval;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d(TAG, "onTouchEvent: " + event);
        mVelocityTracker.addMovement(event);
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                if (!mScroller.isFinished()) {
                    mScroller.abortAnimation();
                }
                mAboveOval = y < mOvalCenterY;
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                int deltaX = x - mLastX;
                int deltaY = y - mLastY;

                int angle = deltaX * 90 / mOvalLongAxis;
                rotateBy(mAboveOval ? angle : -angle);
                break;
            }
            case MotionEvent.ACTION_UP: {
                int rotateAngle = getRotateAngle();
                mVelocityTracker.computeCurrentVelocity(1000);
                float xVelocity = mVelocityTracker.getXVelocity();
                mVelocityTracker.clear();
                break;
            }
            default:
                break;
        }
        mLastX = x;
        mLastY = y;
        return true;
    }

    public void rotateBy(int angle) {
        if (angle != 0) {
            mRotateAngle = (mRotateAngle + angle + 360) % 360;
            postInvalidateOnAnimation();
        }
    }

    public void setDrawableIds(Context context, int[] ids, String[] descriptions) {
        updateDrawableIds(context, ids);
        invalidate();
    }

    private void updateDrawableIds(Context context, int[] ids) {
        mDrawableCount = checkRange("length of ids", ids.length, 1, MAX_PARTS);
        mDrawableList.clear();
        for (int i = 0; i < mDrawableCount; i++) {
            int color = 0;
            if (ids[i] < 0) {
                color = ids[i];
            }
            DrawableInfo drawableInfo = new DrawableInfo(i, color, ids[i]);
            mDrawableList.add(drawableInfo);
        }
    }

    public int getRotateAngle() {
        return mRotateAngle;
    }

    public void setRotateAngle(int rotateAngle) {
        mRotateAngle = rotateAngle;
        invalidate();
    }

    // 以y轴负方向为角度原点，图片缩小一半，顺时针旋转，180度为最大点
    private int[] spiltRoundToAngles(int baseAngle, int parts) {
        checkRange("parts", parts, 1, MAX_PARTS);
        int[] ret = new int[parts];
        for (int i = 0; i < parts; i++) {
            ret[i] = (baseAngle + 360 * i / parts) % 360;
        }
        return ret;
    }

    private int checkRange(String parameterName, int input, int low, int high) {
        if (input < low || input > high) {
            throw new IllegalArgumentException(parameterName + " should between " + low + " and " + high + ", but got " + input);
        }
        return input;
    }

    private static class DrawableInfo {
        // 绘制时确定
        int top;
        int bottom;
        int left;
        int right;
        int centerX;
        int centerY;

        int index;
        int color = 0;
        int drawableId = 0;
        String description;
        int realAngle;        // 实际角度
        int orderAngle;       // 排序用角度
        double rateOnY;          // y坐标比例

        public DrawableInfo(int index, int color, int drawableId) {
            this.index = index;
            this.color = color;
            this.drawableId = drawableId;
        }

        void setRealAngle(int realAngle) {
            this.realAngle = realAngle;
            if (realAngle <= 180) {
                orderAngle = realAngle;
            } else {
                orderAngle = 360 - realAngle;
            }
            rateOnY = orderAngle / 180.0;
        }

        void setPosition(int left, int top, int right, int bottom) {
            this.left = left;
            this.top = top;
            this.right = right;
            this.bottom = bottom;
        }

        void setPosition(int width, int height) {
            left = centerX - width / 2;
            right = left + width;
            top = centerY - height / 2;
            bottom = top + height;
        }

        @Override
        public String toString() {
            return "DrawableInfo[index=" + index
                    + ", centerX=" + centerX
                    + ", centerY=" + centerY
                    + ", left=" + left
                    + ", top=" + top
                    + ", right=" + right
                    + ", bottom=" + bottom;
        }
    }
}
